package com.boyicloud.exchangeconsole.viewmodel

import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.TypeReference
import com.boyicloud.exchangeconsole.commnon.*
import com.boyicloud.exchangeconsole.domain.ClientInfo
import javafx.beans.property.Property
import javafx.beans.property.SimpleObjectProperty
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import javafx.scene.Node
import javafx.scene.control.Alert
import javafx.scene.control.ContextMenu
import javafx.scene.control.Menu
import javafx.scene.control.MenuItem
import javafx.scene.image.Image
import javafx.scene.image.ImageView
import kotlinx.coroutines.*
import kotlinx.coroutines.javafx.JavaFx
import okhttp3.*
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component

@Component
class IndexViewModel(@Autowired val httpClient: HttpClient) {

    var selectModel: Property<ClientInfo> = SimpleObjectProperty(null)

    lateinit var contextMenu: ContextMenu

    var isRetryConnecting = true


    val loading: Property<Node> = SimpleObjectProperty(ImageView())

    val equipmentList: ObservableList<ClientInfo> = FXCollections.observableArrayList()

    val softwareList: ObservableList<ClientInfo> = FXCollections.observableArrayList()

    val log: Logger = LoggerFactory.getLogger(IndexViewModel::class.java)

    var mWebSocket: WebSocket? = null

    var scope = MainScope()

    private val request: Request = Request.Builder()
        .url(Constant.WS_URL)
        .build()

    private val client = OkHttpClient()

    fun refresh() {
        scope.launch(Dispatchers.IO) {
            try {
                withContext(Dispatchers.JavaFx) {
                    loading.value =
                        ImageView(Image(IndexViewModel::class.java.getResourceAsStream("/images/loading.gif")))
                }
                val listResp = httpClient.list()
                withContext(Dispatchers.JavaFx) {
                    this@IndexViewModel.equipmentList.setAll(listResp.equipmentList)
                    this@IndexViewModel.softwareList.setAll(listResp.softwareList)
                }
                val exchangeItem = buildExchangeItem()
                withContext(Dispatchers.JavaFx) {
                    contextMenu.items[1] = exchangeItem
                }
                if (isRetryConnecting) {
                    connectWs()
                }
            } catch (e: Exception) {
                log.warn(e.localizedMessage, e)
                withContext(Dispatchers.JavaFx) {
                    AlertUtil.show(Alert.AlertType.WARNING, "获取服务数据失败，请检查网络")
                }
            } finally {
                withContext(Dispatchers.JavaFx) { loading.value = ImageView() }
            }
        }
    }

    fun buildContextMenu() {
        contextMenu = ContextMenu().apply {
            val offlineItem = MenuItem("下线").apply {
                setOnAction {
                    if (checkNotSelected()) return@setOnAction
                    offline(arrayOf(selectModel.value.id))
                }
            }
            val exchangeItem = buildExchangeItem()
            val disconnectItem = MenuItem("断开").apply {
                setOnAction {
                    if (checkNotSelected()) return@setOnAction
                    disconnect(selectModel.value.id)
                }
            }
            items.addAll(offlineItem, exchangeItem, disconnectItem)
        }
    }

    private fun buildExchangeItem(): Menu {
        return Menu("透传").apply {
            softwareList.forEach {
                val softwareId = it.id
                items.add(MenuItem("${it.ip}:${it.port}").apply {
                    setOnAction {
                        if (checkNotSelected()) return@setOnAction
                        exchange(selectModel.value.id, softwareId)
                    }
                })
            }
        }
    }

    private fun checkNotSelected(): Boolean {
        if (selectModel.value == null) {
            AlertUtil.show(Alert.AlertType.WARNING, "请先选择要操作的设备")
            return true
        }
        return false
    }

    fun offline(ids: Array<Long>) {
        scope.launch(Dispatchers.IO) {
            try {
                val message = httpClient.offline(ids)
                refresh()
                withContext(Dispatchers.JavaFx) {
                    AlertUtil.show(Alert.AlertType.INFORMATION, message)
                }
            } catch (e: Exception) {
                log.warn(e.message, e)
                withContext(Dispatchers.JavaFx) {
                    AlertUtil.show(Alert.AlertType.WARNING, "获取服务数据失败，请检查网络")
                }
            }
        }
    }

    fun exchange(equipmentId: Long, softwareId: Long) {
        scope.launch(Dispatchers.IO) {
            try {
                val message = httpClient.exchange(equipmentId, softwareId)
                refresh()
                withContext(Dispatchers.JavaFx) {
                    AlertUtil.show(Alert.AlertType.INFORMATION, message)
                }
            } catch (e: Exception) {
                log.warn(e.message, e)
                withContext(Dispatchers.JavaFx) {
                    AlertUtil.show(Alert.AlertType.WARNING, "获取服务数据失败，请检查网络")
                }
            }
        }
    }

    private fun disconnect(id: Long) {
        scope.launch(Dispatchers.IO) {
            try {
                val message = httpClient.disconnect(id)
                refresh()
                withContext(Dispatchers.JavaFx) {
                    AlertUtil.show(Alert.AlertType.INFORMATION, message)
                }
            } catch (e: Exception) {
                log.warn(e.message, e)
                withContext(Dispatchers.JavaFx) {
                    AlertUtil.show(Alert.AlertType.WARNING, "获取服务数据失败，请检查网络")
                }
            }
        }
    }

    fun reset() {
        scope.launch(Dispatchers.IO) {
            try {
                val message = httpClient.reset()
                refresh()
                withContext(Dispatchers.JavaFx) {
                    AlertUtil.show(Alert.AlertType.INFORMATION, message)
                }
            } catch (e: Exception) {
                log.warn(e.message, e)
                withContext(Dispatchers.JavaFx) {
                    AlertUtil.show(Alert.AlertType.WARNING, "获取服务数据失败，请检查网络")
                }
            }
        }
    }

    fun connectWs() {
        scope.launch(Dispatchers.IO) {
            mWebSocket = client.newWebSocket(request, object : WebSocketListener() {
                override fun onOpen(webSocket: WebSocket, response: Response) {
                    super.onOpen(webSocket, response)
                    log.info("连接成功...")
                    isRetryConnecting = false
                }

                override fun onMessage(webSocket: WebSocket, text: String) {
                    val result = JSON.parseObject(text, object : TypeReference<Result<ListResp>>() {})
                    val listResp = result.data
                    scope.launch {
                        this@IndexViewModel.equipmentList.setAll(listResp.equipmentList)
                        this@IndexViewModel.softwareList.setAll(listResp.softwareList)
                    }
                    val exchangeItem = buildExchangeItem()
                    scope.launch {
                        contextMenu.items[1] = exchangeItem
                    }
                }

                override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
                    super.onFailure(webSocket, t, response)
                    log.info("连接失败...")
                    isRetryConnecting = true
                }
            })
        }
    }

    fun destroy() {
        mWebSocket?.close(1000, "客户端关闭")
        client.dispatcher().executorService().shutdown()
        scope.cancel()
    }
}





